package cn.zch.test.device;
import cn.zch.util.StringUtil;
import com.google.common.collect.Iterators;
import io.netty.buffer.ByteBufUtil;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Locale;
import java.util.UUID;
/**
 * TCP监听和分发服务
 *
 * @author 郝威
 */
@Slf4j
public class TcpServer implements Runnable {
  protected Selector selector = null;
  protected ServerSocketChannel ssc = null;
  int i = 0;
  public void startServer() {
    try {
      if (selector != null) {
        selector.close();
      }
      selector = Selector.open();
      if (ssc != null) {
        ssc.close();
      }
      ssc = ServerSocketChannel.open();
      ssc.socket().bind(new InetSocketAddress(10001));
      ssc.configureBlocking(false);
      // register
      ssc.register(selector, SelectionKey.OP_ACCEPT);
      while (true) {
        // selector 线程。select() 会阻塞，直到有客户端连接，或者有消息读入
        selector.select();
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        System.out.println("iterator = " + iterator + " " + Iterators.size(iterator));
        while (iterator.hasNext()) {
          SelectionKey selectionKey = iterator.next();
          System.out.println("selectionKey = " + selectionKey + " " + selectionKey.channel());
          // 删除此消息
          iterator.remove();
          // 并在当前线程内处理
          handleSelectionKey(selectionKey);
        }
      }
    }
    catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }
  public void handleSelectionKey(SelectionKey selectionKey) throws Exception {
    if (selectionKey.isAcceptable()) {
      ServerSocketChannel ssc = (ServerSocketChannel) selectionKey.channel();
      SocketChannel socketChannel = ssc.accept();
      socketChannel.configureBlocking(false);
      // 立即注册一个 OP_READ 的SelectionKey, 接收客户端的消息
      SelectionKey key = socketChannel.register(selector, SelectionKey.OP_READ);
      String uuid = UUID.randomUUID().toString();
      key.attach(uuid);
      // 打印
    }
    else if (selectionKey.isReadable()) {
      // 有消息进来 ByteBuffer byteBuffer = ByteBuffer.allocate(273); 由于新服务器叶县二高增加写入5扇区数据,回复变长至292.
      ByteBuffer byteBuffer = ByteBuffer.allocate(292);
      SocketChannel sc = (SocketChannel) selectionKey.channel();
      int len = 0;
      try {
        len = sc.read(byteBuffer);
      }
      catch (Exception e) {
        String uuid = selectionKey.attachment().toString();
        // 如果read抛出异常，表示连接异常中断，需要关闭 socketChannel
        sc.close();
        if (!e.getMessage().contains("远程主机强迫关闭了一个现有的连接") && !e.getMessage().contains("Connection reset by peer")) {
          log.error(e.getMessage(), e);
        }
      }
      // 如果len>0，表示有输入。如果len==0, 表示输入结束。需要关闭 socketChannel
      if (len > 0) {
        byteBuffer.flip();
        byte[] b = byteBuffer.array();
        byteBuffer.clear();
        // 处理数据
        try {
          if (b.length < 30) {
            return;
          }
          byte[] tmpBuf = null;
          len = StringUtil.hexToInt(28, 29, b);
          if (b.length < 28 + len) {
            return;
          }
          else if (b.length > 28 + len) {
            tmpBuf = Arrays.copyOf(b, 28 + len);
          }
          else {
            tmpBuf = b;
          }
          // 校验
          //          if (!CRC16.compareCRC16(tmpBuf)) {
          //            return;
          //          }
          // CardReaderCommandExec.execCommand(selectionKey, tmpBuf)
          log.info("{}", selectionKey);
          log.info("{}", tmpBuf);
          log.info(ByteBufUtil.hexDump(tmpBuf).toUpperCase(Locale.ROOT));
        }
        catch (Exception e) {
          log.error(e.getMessage(), e);
        }
      }
      else {
        // 输入结束，关闭 socketChannel
        String uuid = selectionKey.attachment().toString();
        sc.close();
      }
      Thread.sleep(100);
    }
  }
  @Override
  public void run() {
    try {
      new TcpServer().startServer();
    }
    catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }
  public static void main(String[] args) {
    new TcpServer().startServer();
  }
}
